Das Ziel ist es, aus dem Datacamp Datensatz [Video Game Sales Date] (https://app.datacamp.com/workspace/datasets/dataset-python-video-games-sales), , folgende Fragestellung / Hypothese zu beantworten:

Fragestellung: “Ist es wahrscheinlicher, dass sich bestimmte Spielgenres in Europa signifikant (Unterschied von 50%) besser verkaufen lassen als im Japanischen und Nordamerikanischen Markt?”

Als Einführung werden wir auf Datacamp folgende Kurse durchgehen:

# Import libraries
library("plotly")
Warning: Paket ‘plotly’ wurde unter R Version 4.1.3 erstellt
Lade nötiges Paket: ggplot2
Warning: Paket ‘ggplot2’ wurde unter R Version 4.1.3 erstellt

Attache Paket: ‘plotly’

Das folgende Objekt ist maskiert ‘package:ggplot2’:

    last_plot

Das folgende Objekt ist maskiert ‘package:stats’:

    filter

Das folgende Objekt ist maskiert ‘package:graphics’:

    layout
library("ggplot2")
library("plyr")
Warning: Paket ‘plyr’ wurde unter R Version 4.1.3 erstellt

Attache Paket: ‘plyr’

Die folgenden Objekte sind maskiert von ‘package:plotly’:

    arrange, mutate, rename, summarise
library("dplyr")
Warning: Paket ‘dplyr’ wurde unter R Version 4.1.3 erstellt

Attache Paket: ‘dplyr’

Die folgenden Objekte sind maskiert von ‘package:plyr’:

    arrange, count, desc, failwith, id, mutate, rename, summarise, summarize

Die folgenden Objekte sind maskiert von ‘package:stats’:

    filter, lag

Die folgenden Objekte sind maskiert von ‘package:base’:

    intersect, setdiff, setequal, union
library("broom")
Warning: Paket ‘broom’ wurde unter R Version 4.1.3 erstellt
# Read csv from folder "data"
df = read.csv("./data/video_games_data.csv")

head(df, 10)

Data Wrangling

Bevor wir mit den Visualisierungen und Modelle beginnen können, müssen wir die Daten säubern. Das heisst es sollte keine Duplikate geben, fehlende Werte sollten korrekt eingetragen werden und Daten, die nicht verwendet werden sollten gelöscht werden.

Es hat “N/A” Werte in den Spalten “Year” und “Publisher”. Diese Werte sollten korrekte “NA” Werte sein, damit sie bei den Visualisierungen und Berechnungen nicht berücksichtigt werden.

# Show rows with "N/A" values
df[grep("N/A", df$Publisher),]
df[grep("N/A", df$Year),]
# Replace "N/A" with "NA"
df[df == "N/A"] <- NA

df <- df %>%
  filter(df$Global_Sales > 0.1)

df
# Check if values have been converted
df %>% 
  summarize(across(everything(), ~sum(is.na(.))))

Da 2017 nur 3 Einträge und 2020 nur 1 Eintrag beinhaltet, werden wir diese Jahren nicht berücksichtigen und aus dem Dataframe löschen, weil sie nicht vollständig sind und so könnten unsere Modelle ungenau werden.

# Remove years 2017 and 2020 from dataset
df_clean <- df[!(df$Year == "2017" | df$Year == "2020"),]
View(df_clean)
# Remove columns: Rank, Game name, Platform, Global Sales because they are not needed for our thesis
# Set data to correct type
df_clean$Genre <- as.factor(df_clean$Genre)
df_clean$Year <- as.numeric(df_clean$Year)

Erste Plots erstellen

na <- sum(df_clean[, 'NA_Sales'], na.rm = TRUE)
eu <- sum(df_clean[, 'EU_Sales'], na.rm = TRUE)
jp <- sum(df_clean[, 'JP_Sales'], na.rm = TRUE)
o <- sum(df_clean[, 'Other_Sales'], na.rm = TRUE)
g <- sum(df_clean[, 'Global_Sales'], na.rm = TRUE)

fig <- plot_ly(
  y = c(na, eu, jp, o), 
  x = c("North America", "Europe", "Japan", "Other"), 
  type = 'bar',
  width = 800
)

fig <- fig %>% layout(title = "Video Game Sales Overview",
         xaxis = list(title = "Region"),
         yaxis = list(title = "Sales (million)"))

fig

Wir sehen, dass Nord Amerika der grösste Markt ist.

# group by genre and summarize game sales to each region
df_genre <- df_clean %>%
  group_by(Genre) %>%
  summarize(
    NA_Sales_Sum = sum(NA_Sales),
    EU_Sales_Sum = sum(EU_Sales), 
    JP_Sales_Sum = sum(JP_Sales),
    Other_Sales_Sum = sum(Other_Sales),
    Global_Sales_Sum = sum(Global_Sales)
  )

# Plot grouped bar chart video game sales by genre
fig <- plot_ly(
  df_genre, y = ~Genre, x = ~NA_Sales_Sum, type = "bar", name = "North America", width = 1000, height = 800) %>% 
  add_trace(x = ~EU_Sales_Sum, name = "Europe") %>%
  add_trace(x = ~JP_Sales_Sum, name = "Japan") %>%
  add_trace(x = ~Other_Sales_Sum, name = "Other") %>%
  layout(
    title = "Video Game Sales by Genre",
    xaxis = list(title = "Sales (million)"),
    barmode = "group"
  )

fig
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations

Japan hat viel mehr Role-Play, Strategie und viel weniger Shooter und Action als die anderen Regionen

df_year <- df_clean %>%
  group_by(Year) %>%
  summarize(
    NA_Sales_Sum = sum(NA_Sales),
    EU_Sales_Sum = sum(EU_Sales), 
    JP_Sales_Sum = sum(JP_Sales),
    Other_Sales_Sum = sum(Other_Sales),
    Global_Sales_Sum = sum(Global_Sales)
  )

fig <- plot_ly(
  df_year, y = ~NA_Sales_Sum, x = ~Year, type = "bar", name = "North America", width = 900, height = 500) %>% 
  add_trace(y = ~EU_Sales_Sum, name = "Europe") %>%
  add_trace(y = ~JP_Sales_Sum, name = "Japan") %>%
  add_trace(y = ~Other_Sales_Sum, name = "Other") %>%
  layout(
    title = "Video Game from Sales by Year",
    xaxis = list(title = "Year"),
    yaxis = list(title = "Sales (million)"),
    barmode = "stack"
  )

fig
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations

Regressionsmodelle

Da wir unsere Daten jetzt besser verstehen, können wir mit den Regressionsmodellen und mit der Beantwortung unserer Fragestellung beginnen.

# Calculate the average of sales of each genre from each region
df_sales_avg <- df_clean %>%
  group_by(Genre) %>%
  summarise(
    EU_Sales_Avg = mean(EU_Sales),
    NA_Sales_Avg = mean(NA_Sales),
    JP_Sales_Avg = mean(JP_Sales),
    Other_Sales_Avg = mean(Other_Sales),
    Global_Sales_Avg = mean(Global_Sales))

df_sales_avg

Kann man anhand der nordamerikanischen Verkäufe

# Create DataFrame only with Action games
genre <- "Racing"

dF_action <- df_clean %>%
  filter(Genre == genre)

ggplot(dF_action, aes(x=EU_Sales, y=NA_Sales)) + 
  geom_point() + 
  geom_smooth(method = "lm", se=FALSE) +
  labs(title = genre)
`geom_smooth()` using formula 'y ~ x'

# Create linear model
mdl_action <- lm(EU_Sales ~ NA_Sales, data = dF_action)

# Extract model score
mdl_action %>%
  glance() %>%
  pull(r.squared)
[1] 0.7601522
# Color the SPLOM of NA_Sales, EU_Sales, and JP_Sales by nintendo
df_cleaner %>%
  #mutate(nintendo = ifelse(Publisher == "Nintendo", "Nintendo", "Other")) %>%
  plot_ly(color = ~Genre) %>% 
  add_trace(
    type = 'splom',                                   
    dimensions = list(
      list(label = 'N. America', values = ~NA_Sales),
      list(label = 'Europe', values = ~EU_Sales),     
      list(label = 'Japan', values = ~JP_Sales)       
    )
  )
Warning in RColorBrewer::brewer.pal(N, "Set2") :
  n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
df_cleaner <- df_clean %>%
  filter(Global_Sales > 0.8)

mdl_eu_global <- lm(EU_Sales ~ Global_Sales, data = df_cleaner)
mdl_eu_global

Call:
lm(formula = EU_Sales ~ Global_Sales, data = df_cleaner)

Coefficients:
 (Intercept)  Global_Sales  
   -0.004866      0.293020  
mdl_eu_global_trans <- lm(sqrt(EU_Sales) ~ sqrt(Global_Sales), data = df_cleaner)
mdl_eu_global_trans

Call:
lm(formula = sqrt(EU_Sales) ~ sqrt(Global_Sales), data = df_cleaner)

Coefficients:
       (Intercept)  sqrt(Global_Sales)  
          -0.06338             0.54523  
summary(mdl_eu_global)

Call:
lm(formula = EU_Sales ~ Global_Sales, data = df_cleaner)

Residuals:
    Min      1Q  Median      3Q     Max 
-8.2063 -0.2264  0.0066  0.1608  4.7804 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -0.004866   0.013175  -0.369    0.712    
Global_Sales  0.293020   0.003166  92.554   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.5399 on 2524 degrees of freedom
Multiple R-squared:  0.7724,    Adjusted R-squared:  0.7723 
F-statistic:  8566 on 1 and 2524 DF,  p-value: < 2.2e-16
mdl_eu_global %>%
  glance() %>%
  pull(sigma)
[1] 0.2751684
ggplot(df_sales_avg, aes(x=NA_Sales_Avg, y=Global_Sales_Avg)) + 
  geom_point() + 
  geom_smooth(method = "lm", se=FALSE) 
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 1 rows containing non-finite values (stat_smooth).
Warning: Removed 1 rows containing missing values (geom_point).

mdl_na_global <- lm(NA_Sales_Avg ~ Global_Sales_Avg, data = df_sales_avg)
mdl_na_global

Call:
lm(formula = NA_Sales_Avg ~ Global_Sales_Avg, data = df_sales_avg)

Coefficients:
     (Intercept)  Global_Sales_Avg  
        -0.07453           0.58093  
na <- tibble(Global_Sales_Avg = 9)

predict(mdl_na_global, na)
       1 
5.153829 
ggplot(df_sales_avg, aes(x=JP_Sales_Avg, y=Global_Sales_Avg)) + 
  geom_point() + 
  geom_smooth(method = "lm", se=FALSE)
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 1 rows containing non-finite values (stat_smooth).
Warning: Removed 1 rows containing missing values (geom_point).

mdl_jp_global <- lm(JP_Sales_Avg ~ Global_Sales_Avg, data = df_sales_avg)
mdl_jp_global

Call:
lm(formula = JP_Sales_Avg ~ Global_Sales_Avg, data = df_sales_avg)

Coefficients:
     (Intercept)  Global_Sales_Avg  
         0.05376           0.09446  
jp <- tibble(Global_Sales_Avg = 9)

predict(mdl_jp_global, jp)
        1 
0.9038834 
# Standardize

#df_clean[7 : 10] <- as.data.frame(scale(df_clean[7 : 10]))

#df_clean
LS0tDQp0aXRsZTogIlJlZ3Jlc3Npb24gbW9kZWxzIHdpdGggUiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCkRhcyBaaWVsIGlzdCBlcywgYXVzIGRlbSBEYXRhY2FtcCBEYXRlbnNhdHogW1ZpZGVvIEdhbWUgU2FsZXMgRGF0ZV0gKGh0dHBzOi8vYXBwLmRhdGFjYW1wLmNvbS93b3Jrc3BhY2UvZGF0YXNldHMvZGF0YXNldC1weXRob24tdmlkZW8tZ2FtZXMtc2FsZXMpLCAsIGZvbGdlbmRlIEZyYWdlc3RlbGx1bmcgLyBIeXBvdGhlc2UgenUgYmVhbnR3b3J0ZW46DQoNCg0KIyMjIEZyYWdlc3RlbGx1bmc6ICJJc3QgZXMgd2FocnNjaGVpbmxpY2hlciwgZGFzcyBzaWNoIGJlc3RpbW10ZSBTcGllbGdlbnJlcyBpbiBFdXJvcGEgc2lnbmlmaWthbnQgKFVudGVyc2NoaWVkIHZvbiA1MCUpIGJlc3NlciB2ZXJrYXVmZW4gbGFzc2VuIGFscyBpbSBKYXBhbmlzY2hlbiB1bmQgTm9yZGFtZXJpa2FuaXNjaGVuIE1hcmt0PyINCg0KDQpBbHMgRWluZsO8aHJ1bmcgd2VyZGVuIHdpciBhdWYgRGF0YWNhbXAgZm9sZ2VuZGUgS3Vyc2UgZHVyY2hnZWhlbjoNCg0KLSBbSW50cm9kdWN0aW9uIHRvIFJlZ3Jlc3Npb24gaW4gUl0oaHR0cHM6Ly9hcHAuZGF0YWNhbXAuY29tL2xlYXJuL2NvdXJzZXMvaW50cm9kdWN0aW9uLXRvLXJlZ3Jlc3Npb24taW4tcikNCg0KLSBbSW50ZXJtZWRpYXRlIFJlZ3Jlc3Npb24gaW4gUl0oaHR0cHM6Ly9hcHAuZGF0YWNhbXAuY29tL2xlYXJuL2NvdXJzZXMvaW50ZXJtZWRpYXRlLXJlZ3Jlc3Npb24taW4tcikNCg0KDQpgYGB7cn0NCiMgSW1wb3J0IGxpYnJhcmllcw0KbGlicmFyeSgicGxvdGx5IikNCmxpYnJhcnkoImdncGxvdDIiKQ0KbGlicmFyeSgicGx5ciIpDQpsaWJyYXJ5KCJkcGx5ciIpDQpsaWJyYXJ5KCJicm9vbSIpDQpgYGANCg0KYGBge3J9DQojIFJlYWQgY3N2IGZyb20gZm9sZGVyICJkYXRhIg0KZGYgPSByZWFkLmNzdigiLi9kYXRhL3ZpZGVvX2dhbWVzX2RhdGEuY3N2IikNCg0KaGVhZChkZiwgMTApDQpgYGANCiMjIyBEYXRhIFdyYW5nbGluZw0KQmV2b3Igd2lyIG1pdCBkZW4gVmlzdWFsaXNpZXJ1bmdlbiB1bmQgTW9kZWxsZSBiZWdpbm5lbiBrw7ZubmVuLCBtw7xzc2VuIHdpciBkaWUgRGF0ZW4gc8OkdWJlcm4uIERhcyBoZWlzc3QgZXMgc29sbHRlIGtlaW5lIER1cGxpa2F0ZSBnZWJlbiwgZmVobGVuZGUgV2VydGUgc29sbHRlbiBrb3JyZWt0IGVpbmdldHJhZ2VuIHdlcmRlbiB1bmQgRGF0ZW4sIGRpZSBuaWNodCB2ZXJ3ZW5kZXQgd2VyZGVuIHNvbGx0ZW4gZ2Vsw7ZzY2h0IHdlcmRlbi4NCg0KRXMgaGF0ICJOL0EiIFdlcnRlIGluIGRlbiBTcGFsdGVuICJZZWFyIiB1bmQgIlB1Ymxpc2hlciIuIERpZXNlIFdlcnRlIHNvbGx0ZW4ga29ycmVrdGUgIk5BIiBXZXJ0ZSBzZWluLCBkYW1pdCBzaWUgYmVpIGRlbiBWaXN1YWxpc2llcnVuZ2VuIHVuZCBCZXJlY2hudW5nZW4gbmljaHQgYmVyw7xja3NpY2h0aWd0IHdlcmRlbi4NCg0KYGBge3J9DQojIFNob3cgcm93cyB3aXRoICJOL0EiIHZhbHVlcw0KZGZbZ3JlcCgiTi9BIiwgZGYkUHVibGlzaGVyKSxdDQpkZltncmVwKCJOL0EiLCBkZiRZZWFyKSxdDQpgYGANCg0KYGBge3J9DQojIFJlcGxhY2UgIk4vQSIgd2l0aCAiTkEiDQpkZltkZiA9PSAiTi9BIl0gPC0gTkENCg0KZGYgPC0gZGYgJT4lDQogIGZpbHRlcihkZiRHbG9iYWxfU2FsZXMgPiAwLjEpDQoNCmRmDQpgYGANCg0KYGBge3J9DQojIENoZWNrIGlmIHZhbHVlcyBoYXZlIGJlZW4gY29udmVydGVkDQpkZiAlPiUgDQogIHN1bW1hcml6ZShhY3Jvc3MoZXZlcnl0aGluZygpLCB+c3VtKGlzLm5hKC4pKSkpDQpgYGANCkRhIDIwMTcgbnVyIDMgRWludHLDpGdlIHVuZCAyMDIwIG51ciAxIEVpbnRyYWcgYmVpbmhhbHRldCwgd2VyZGVuIHdpciBkaWVzZSBKYWhyZW4gbmljaHQgYmVyw7xja3NpY2h0aWdlbiB1bmQgYXVzIGRlbSBEYXRhZnJhbWUgbMO2c2NoZW4sIHdlaWwgc2llIG5pY2h0IHZvbGxzdMOkbmRpZyBzaW5kIHVuZCBzbyBrw7ZubnRlbiB1bnNlcmUgTW9kZWxsZSB1bmdlbmF1IHdlcmRlbi4NCg0KYGBge3J9DQojIFJlbW92ZSB5ZWFycyAyMDE3IGFuZCAyMDIwIGZyb20gZGF0YXNldA0KZGZfY2xlYW4gPC0gZGZbIShkZiRZZWFyID09ICIyMDE3IiB8IGRmJFllYXIgPT0gIjIwMjAiKSxdDQpWaWV3KGRmX2NsZWFuKQ0KYGBgDQoNCmBgYHtyfQ0KIyBSZW1vdmUgY29sdW1uczogUmFuaywgR2FtZSBuYW1lLCBQbGF0Zm9ybSwgR2xvYmFsIFNhbGVzIGJlY2F1c2UgdGhleSBhcmUgbm90IG5lZWRlZCBmb3Igb3VyIHRoZXNpcw0KYGBgDQoNCmBgYHtyfQ0KIyBTZXQgZGF0YSB0byBjb3JyZWN0IHR5cGUNCmRmX2NsZWFuJEdlbnJlIDwtIGFzLmZhY3RvcihkZl9jbGVhbiRHZW5yZSkNCmRmX2NsZWFuJFllYXIgPC0gYXMubnVtZXJpYyhkZl9jbGVhbiRZZWFyKQ0KYGBgDQoNCiMjIyBFcnN0ZSBQbG90cyBlcnN0ZWxsZW4NCg0KYGBge3J9DQpuYSA8LSBzdW0oZGZfY2xlYW5bLCAnTkFfU2FsZXMnXSwgbmEucm0gPSBUUlVFKQ0KZXUgPC0gc3VtKGRmX2NsZWFuWywgJ0VVX1NhbGVzJ10sIG5hLnJtID0gVFJVRSkNCmpwIDwtIHN1bShkZl9jbGVhblssICdKUF9TYWxlcyddLCBuYS5ybSA9IFRSVUUpDQpvIDwtIHN1bShkZl9jbGVhblssICdPdGhlcl9TYWxlcyddLCBuYS5ybSA9IFRSVUUpDQpnIDwtIHN1bShkZl9jbGVhblssICdHbG9iYWxfU2FsZXMnXSwgbmEucm0gPSBUUlVFKQ0KDQpmaWcgPC0gcGxvdF9seSgNCiAgeSA9IGMobmEsIGV1LCBqcCwgbyksIA0KICB4ID0gYygiTm9ydGggQW1lcmljYSIsICJFdXJvcGUiLCAiSmFwYW4iLCAiT3RoZXIiKSwgDQogIHR5cGUgPSAnYmFyJywNCiAgd2lkdGggPSA4MDANCikNCg0KZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gIlZpZGVvIEdhbWUgU2FsZXMgT3ZlcnZpZXciLA0KICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIlJlZ2lvbiIpLA0KICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlNhbGVzIChtaWxsaW9uKSIpKQ0KDQpmaWcNCmBgYA0KV2lyIHNlaGVuLCBkYXNzIE5vcmQgQW1lcmlrYSBkZXIgZ3LDtnNzdGUgTWFya3QgaXN0LiANCg0KYGBge3J9DQojIGdyb3VwIGJ5IGdlbnJlIGFuZCBzdW1tYXJpemUgZ2FtZSBzYWxlcyB0byBlYWNoIHJlZ2lvbg0KZGZfZ2VucmUgPC0gZGZfY2xlYW4gJT4lDQogIGdyb3VwX2J5KEdlbnJlKSAlPiUNCiAgc3VtbWFyaXplKA0KICAgIE5BX1NhbGVzX1N1bSA9IHN1bShOQV9TYWxlcyksDQogICAgRVVfU2FsZXNfU3VtID0gc3VtKEVVX1NhbGVzKSwgDQogICAgSlBfU2FsZXNfU3VtID0gc3VtKEpQX1NhbGVzKSwNCiAgICBPdGhlcl9TYWxlc19TdW0gPSBzdW0oT3RoZXJfU2FsZXMpLA0KICAgIEdsb2JhbF9TYWxlc19TdW0gPSBzdW0oR2xvYmFsX1NhbGVzKQ0KICApDQoNCiMgUGxvdCBncm91cGVkIGJhciBjaGFydCB2aWRlbyBnYW1lIHNhbGVzIGJ5IGdlbnJlDQpmaWcgPC0gcGxvdF9seSgNCiAgZGZfZ2VucmUsIHkgPSB+R2VucmUsIHggPSB+TkFfU2FsZXNfU3VtLCB0eXBlID0gImJhciIsIG5hbWUgPSAiTm9ydGggQW1lcmljYSIsIHdpZHRoID0gMTAwMCwgaGVpZ2h0ID0gODAwKSAlPiUgDQogIGFkZF90cmFjZSh4ID0gfkVVX1NhbGVzX1N1bSwgbmFtZSA9ICJFdXJvcGUiKSAlPiUNCiAgYWRkX3RyYWNlKHggPSB+SlBfU2FsZXNfU3VtLCBuYW1lID0gIkphcGFuIikgJT4lDQogIGFkZF90cmFjZSh4ID0gfk90aGVyX1NhbGVzX1N1bSwgbmFtZSA9ICJPdGhlciIpICU+JQ0KICBsYXlvdXQoDQogICAgdGl0bGUgPSAiVmlkZW8gR2FtZSBTYWxlcyBieSBHZW5yZSIsDQogICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIlNhbGVzIChtaWxsaW9uKSIpLA0KICAgIGJhcm1vZGUgPSAiZ3JvdXAiDQogICkNCg0KZmlnDQpgYGANCkphcGFuIGhhdCB2aWVsIG1laHIgUm9sZS1QbGF5LCBTdHJhdGVnaWUgdW5kIHZpZWwgd2VuaWdlciBTaG9vdGVyIHVuZCBBY3Rpb24gYWxzIGRpZSBhbmRlcmVuIFJlZ2lvbmVuDQoNCmBgYHtyfQ0KZGZfeWVhciA8LSBkZl9jbGVhbiAlPiUNCiAgZ3JvdXBfYnkoWWVhcikgJT4lDQogIHN1bW1hcml6ZSgNCiAgICBOQV9TYWxlc19TdW0gPSBzdW0oTkFfU2FsZXMpLA0KICAgIEVVX1NhbGVzX1N1bSA9IHN1bShFVV9TYWxlcyksIA0KICAgIEpQX1NhbGVzX1N1bSA9IHN1bShKUF9TYWxlcyksDQogICAgT3RoZXJfU2FsZXNfU3VtID0gc3VtKE90aGVyX1NhbGVzKSwNCiAgICBHbG9iYWxfU2FsZXNfU3VtID0gc3VtKEdsb2JhbF9TYWxlcykNCiAgKQ0KDQpmaWcgPC0gcGxvdF9seSgNCiAgZGZfeWVhciwgeSA9IH5OQV9TYWxlc19TdW0sIHggPSB+WWVhciwgdHlwZSA9ICJiYXIiLCBuYW1lID0gIk5vcnRoIEFtZXJpY2EiLCB3aWR0aCA9IDkwMCwgaGVpZ2h0ID0gNTAwKSAlPiUgDQogIGFkZF90cmFjZSh5ID0gfkVVX1NhbGVzX1N1bSwgbmFtZSA9ICJFdXJvcGUiKSAlPiUNCiAgYWRkX3RyYWNlKHkgPSB+SlBfU2FsZXNfU3VtLCBuYW1lID0gIkphcGFuIikgJT4lDQogIGFkZF90cmFjZSh5ID0gfk90aGVyX1NhbGVzX1N1bSwgbmFtZSA9ICJPdGhlciIpICU+JQ0KICBsYXlvdXQoDQogICAgdGl0bGUgPSAiVmlkZW8gR2FtZSBmcm9tIFNhbGVzIGJ5IFllYXIiLA0KICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJZZWFyIiksDQogICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlNhbGVzIChtaWxsaW9uKSIpLA0KICAgIGJhcm1vZGUgPSAic3RhY2siDQogICkNCg0KZmlnDQpgYGANCg0KIyMjIFJlZ3Jlc3Npb25zbW9kZWxsZQ0KDQpEYSB3aXIgdW5zZXJlIERhdGVuIGpldHp0IGJlc3NlciB2ZXJzdGVoZW4sIGvDtm5uZW4gd2lyIG1pdCBkZW4gUmVncmVzc2lvbnNtb2RlbGxlbiB1bmQgbWl0IGRlciBCZWFudHdvcnR1bmcgdW5zZXJlciBGcmFnZXN0ZWxsdW5nIGJlZ2lubmVuLg0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRoZSBhdmVyYWdlIG9mIHNhbGVzIG9mIGVhY2ggZ2VucmUgZnJvbSBlYWNoIHJlZ2lvbg0KZGZfc2FsZXNfYXZnIDwtIGRmX2NsZWFuICU+JQ0KICBncm91cF9ieShHZW5yZSkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBFVV9TYWxlc19BdmcgPSBtZWFuKEVVX1NhbGVzKSwNCiAgICBOQV9TYWxlc19BdmcgPSBtZWFuKE5BX1NhbGVzKSwNCiAgICBKUF9TYWxlc19BdmcgPSBtZWFuKEpQX1NhbGVzKSwNCiAgICBPdGhlcl9TYWxlc19BdmcgPSBtZWFuKE90aGVyX1NhbGVzKSwNCiAgICBHbG9iYWxfU2FsZXNfQXZnID0gbWVhbihHbG9iYWxfU2FsZXMpKQ0KDQpkZl9zYWxlc19hdmcNCmBgYA0KS2FubiBtYW4gYW5oYW5kIGRlciBub3JkYW1lcmlrYW5pc2NoZW4gVmVya8OkdWZlIA0KDQpgYGB7cn0NCiMgQ3JlYXRlIERhdGFGcmFtZSBvbmx5IHdpdGggQWN0aW9uIGdhbWVzDQpnZW5yZSA8LSAiUmFjaW5nIg0KDQpkRl9hY3Rpb24gPC0gZGZfY2xlYW4gJT4lDQogIGZpbHRlcihHZW5yZSA9PSBnZW5yZSkNCg0KZ2dwbG90KGRGX2FjdGlvbiwgYWVzKHg9RVVfU2FsZXMsIHk9TkFfU2FsZXMpKSArIA0KICBnZW9tX3BvaW50KCkgKyANCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9RkFMU0UpICsNCiAgbGFicyh0aXRsZSA9IGdlbnJlKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDcmVhdGUgbGluZWFyIG1vZGVsDQptZGxfYWN0aW9uIDwtIGxtKEVVX1NhbGVzIH4gTkFfU2FsZXMsIGRhdGEgPSBkRl9hY3Rpb24pDQoNCiMgRXh0cmFjdCBtb2RlbCBzY29yZQ0KbWRsX2FjdGlvbiAlPiUNCiAgZ2xhbmNlKCkgJT4lDQogIHB1bGwoci5zcXVhcmVkKQ0KYGBgDQoNCg0KYGBge3J9DQojIENvbG9yIHRoZSBTUExPTSBvZiBOQV9TYWxlcywgRVVfU2FsZXMsIGFuZCBKUF9TYWxlcyBieSBuaW50ZW5kbw0KZGZfY2xlYW5lciAlPiUNCiAgI211dGF0ZShuaW50ZW5kbyA9IGlmZWxzZShQdWJsaXNoZXIgPT0gIk5pbnRlbmRvIiwgIk5pbnRlbmRvIiwgIk90aGVyIikpICU+JQ0KICBwbG90X2x5KGNvbG9yID0gfkdlbnJlKSAlPiUgDQogIGFkZF90cmFjZSgNCiAgICB0eXBlID0gJ3NwbG9tJywgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgIGRpbWVuc2lvbnMgPSBsaXN0KA0KICAgICAgbGlzdChsYWJlbCA9ICdOLiBBbWVyaWNhJywgdmFsdWVzID0gfk5BX1NhbGVzKSwNCiAgICAgIGxpc3QobGFiZWwgPSAnRXVyb3BlJywgdmFsdWVzID0gfkVVX1NhbGVzKSwgICAgIA0KICAgICAgbGlzdChsYWJlbCA9ICdKYXBhbicsIHZhbHVlcyA9IH5KUF9TYWxlcykgICAgICAgDQogICAgKQ0KICApDQpgYGANCg0KDQoNCmBgYHtyfQ0KZGZfY2xlYW5lciA8LSBkZl9jbGVhbiAlPiUNCiAgZmlsdGVyKEdsb2JhbF9TYWxlcyA+IDAuNSkNCg0KbWRsX2V1X2dsb2JhbCA8LSBsbShFVV9TYWxlcyB+IEdsb2JhbF9TYWxlcywgZGF0YSA9IGRmX2NsZWFuZXIpDQptZGxfZXVfZ2xvYmFsDQoNCm1kbF9ldV9nbG9iYWxfdHJhbnMgPC0gbG0oc3FydChFVV9TYWxlcykgfiBzcXJ0KEdsb2JhbF9TYWxlcyksIGRhdGEgPSBkZl9jbGVhbmVyKQ0KbWRsX2V1X2dsb2JhbF90cmFucw0KYGBgDQpgYGB7cn0NCnN1bW1hcnkobWRsX2V1X2dsb2JhbCkNCmBgYA0KDQpgYGB7cn0NCm1kbF9ldV9nbG9iYWwgJT4lDQogIGdsYW5jZSgpICU+JQ0KICBwdWxsKHNpZ21hKQ0KYGBgDQoNCg0KYGBge3J9DQpnZ3Bsb3QoZGZfc2FsZXNfYXZnLCBhZXMoeD1OQV9TYWxlc19BdmcsIHk9R2xvYmFsX1NhbGVzX0F2ZykpICsgDQogIGdlb21fcG9pbnQoKSArIA0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT1GQUxTRSkgDQpgYGANCg0KYGBge3J9DQptZGxfbmFfZ2xvYmFsIDwtIGxtKE5BX1NhbGVzX0F2ZyB+IEdsb2JhbF9TYWxlc19BdmcsIGRhdGEgPSBkZl9zYWxlc19hdmcpDQptZGxfbmFfZ2xvYmFsDQoNCm5hIDwtIHRpYmJsZShHbG9iYWxfU2FsZXNfQXZnID0gOSkNCg0KcHJlZGljdChtZGxfbmFfZ2xvYmFsLCBuYSkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChkZl9zYWxlc19hdmcsIGFlcyh4PUpQX1NhbGVzX0F2ZywgeT1HbG9iYWxfU2FsZXNfQXZnKSkgKyANCiAgZ2VvbV9wb2ludCgpICsgDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFKQ0KYGBgDQoNCmBgYHtyfQ0KbWRsX2pwX2dsb2JhbCA8LSBsbShKUF9TYWxlc19BdmcgfiBHbG9iYWxfU2FsZXNfQXZnLCBkYXRhID0gZGZfc2FsZXNfYXZnKQ0KbWRsX2pwX2dsb2JhbA0KDQpqcCA8LSB0aWJibGUoR2xvYmFsX1NhbGVzX0F2ZyA9IDkpDQoNCnByZWRpY3QobWRsX2pwX2dsb2JhbCwganApDQpgYGANCg0KYGBge3J9DQojIFN0YW5kYXJkaXplDQoNCiNkZl9jbGVhbls3IDogMTBdIDwtIGFzLmRhdGEuZnJhbWUoc2NhbGUoZGZfY2xlYW5bNyA6IDEwXSkpDQoNCiNkZl9jbGVhbg0KYGBgDQoNCg0K